/**
 * 
 */
package com.example.net1;


import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableEntryException;
import java.security.cert.CertificateException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Random;

import javax.crypto.BadPaddingException;
import javax.crypto.SecretKey;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;


public class SDFSMultiServerThread extends Thread {
	
	
	public static final int PORT_BANK = 8888;	
	public static final String clientReq=  "Hello message";
	public static final String bankInformation = "Bank Information";
	public static final String CashDeskReqNonce =  "getNonce";	
	public static final String keyStore = "./keystore.jks";
	public static final String cert = "./serverCA.crt";
		
	
	private InputStream bis = null;
	private OutputStream bos = null;
	ObjectOutputStream boos = null;
	ObjectInputStream bois = null;

	
	static Long generateNounce(){
		return generateLong();					
	}

	static Long generateReceiptNumber(){
		return generateLong();				
	}
	
	static Long generateLong(){
		return Math.abs(new Random(System.currentTimeMillis()).nextLong());			
	}
	
	
	public double getClientBalance(Connection con, BankInformation bankinfo ) throws SQLException{
		 double balance =0;
		 Statement stmt = con.createStatement();		 
		    //Execute the SQL statement and get the results in a Resultset
		   String query = "select Balance from  Accounts WHERE Firstname ='" + bankinfo.firstName + "' AND Lastname ='"+bankinfo.lastName+"' AND CardNum='"+bankinfo.cardNumber+"';";
		   ResultSet rs = stmt.executeQuery(query);
		   while (rs.next ()){
			   balance = rs.getDouble("Balance");
		   } 
		   return balance;
	}
	
	public double getCashDeskBalance(Connection con, long id ) throws SQLException{
		 double balance =0;
		 Statement stmt = con.createStatement();		 
		    //Execute the SQL statement and get the results in a Resultset
		   String query = "select Balance from  Accounts WHERE  CardNum='"+id+"';";
		   ResultSet rs = stmt.executeQuery(query);
		   while (rs.next ()){
			   balance = rs.getDouble("Balance");
		   } 
		   return balance;
	}
	
	public void updateBalancCashDesk(Connection con, long id, double newBlanace ) throws SQLException{
		double balance =0;
		Statement stmt = con.createStatement();		 
		//Execute the SQL statement and get the results in a Resultset
		String query = "UPDATE Accounts SET  Balance ="+newBlanace+"WHERE CardNum='"+id+"';";
		stmt.executeUpdate(query);		
		
	}
	
	public void updateBalance(Connection con, BankInformation bankinfo, double newBlanace ) throws SQLException{
		double balance =0;
		Statement stmt = con.createStatement();		 
		//Execute the SQL statement and get the results in a Resultset
		String query = "UPDATE Accounts SET  Balance ="+newBlanace+"WHERE Firstname ='" + bankinfo.firstName + "' AND Lastname ='"+bankinfo.lastName+"' AND CardNum='"+bankinfo.cardNumber+"';";
		stmt.executeUpdate(query);		
		
	}
		
	
	
	boolean checkBankInfo(EncryptedObject bankInfo) throws BadPaddingException, NoSuchAlgorithmException, CertificateException, KeyStoreException, UnrecoverableEntryException, ClassNotFoundException, IOException, SQLException{
		if(bankInfo!= null){
			SecretKey aes2 = CryptoUtil.decryptKey(bankInfo.encryptedKey, CryptoUtil.getRSAprivateKey(keyStore));
			BankTicket bankTicket = (BankTicket) CryptoUtil.decrypt(bankInfo.encryptedObject, aes2);
			SecretKey aes1 = CryptoUtil.decryptKey(bankTicket.encryptedBankInformation.encryptedKey, CryptoUtil.getRSAprivateKey(keyStore));
			BankInformation bankinfo = (BankInformation) CryptoUtil.decrypt(bankTicket.encryptedBankInformation.encryptedObject, aes1);
			System.out.println(bankinfo);
			Connection con = DBConnect.connect();
			double clientBalance = getClientBalance(con, bankinfo);
			System.out.println("Client Balance: "+clientBalance);			
			PaymentRequest paymentRequest = (PaymentRequest)bankTicket.signedPaymentRequest.getObject();
			System.out.println("Client ticket Total: "+paymentRequest.amount);
			//paymentRequest.			
			if(paymentRequest.amount<clientBalance){
				double newbalace = clientBalance-paymentRequest.amount;
				updateBalance(con, bankinfo, newbalace);
				double cashDeskBalance= getCashDeskBalance(con, paymentRequest.sellerId);
				updateBalancCashDesk(con, paymentRequest.sellerId, cashDeskBalance+paymentRequest.amount);
				return true;
			}	
			 return false;
		}
		else return false;		
	}
	public void disconnectBank() {
		try {
			bos.close();
			boos.close();
			bis.close();
			bois.close();
			clientSocket.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	private void listenToClient(){		
		try {
		//	serverSocket = new ServerSocket(PORT_BANK);
		//	System.out.println("Listening :"+PORT_BANK);
		//	clientSocket = serverSocket.accept();
			bis =  clientSocket.getInputStream();
			bos =  clientSocket.getOutputStream();
			System.out.println("ip: " + clientSocket.getInetAddress());	
			boos = new ObjectOutputStream(bos);  	
			bois = new ObjectInputStream(bis);  	
		}
		catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	private void handleNonce() throws IOException, ClassNotFoundException{
		String message = (String)bois.readObject(); 
		System.out.println(message);
		if(message.equals(CashDeskReqNonce)){
			 boos.writeObject(generateNounce());	
			 boos.flush();
		}			
	}
	
	public void handleBankInformation() throws IOException, ClassNotFoundException, BadPaddingException, NoSuchAlgorithmException, CertificateException, KeyStoreException, UnrecoverableEntryException, SQLException{		
		EncryptedObject BankInformnation = (EncryptedObject)bois.readObject();
		if(checkBankInfo(BankInformnation)){
			boos.writeObject("OK:"+generateReceiptNumber());	
			boos.flush();
		}
		else{
			boos.writeObject("KO:"+-1);;	
			boos.flush();
		}			
	}

	private SSLSocket clientSocket = null;
	private boolean certificatOK = true;
	SSLSession sslSession = null;
	String clientID = "";

	public SDFSMultiServerThread(SSLSocket socket) {
		super("SDFSMultiServerThread");
		this.clientSocket = socket;
		this.sslSession = clientSocket.getSession();				
	}

	public void run() {	
		listenToClient();
		try {
			handleNonce();
			handleBankInformation();
			disconnectBank();			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (BadPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (CertificateException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (KeyStoreException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (UnrecoverableEntryException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}		
		System.out.println("*****Server thread off.");
		
	}	
}